package com.spyu.proxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Objects;

/**
 * Jdk代理工具类
 *
 * @author spyu
 * @date 2023-03-08
 */
public class JdkProxyUtils {

    /**
     * 获取指定对象的代理
     * @param t
     * @param <T>
     * @return
     */
    public static <T> Object getProxy(T t) {
        ClassLoader classLoader = t.getClass().getClassLoader();
        Class<?>[] interfaces = t.getClass().getInterfaces();

        if (Objects.isNull(interfaces) || interfaces.length == 0) {
            throw new RuntimeException("实现的jdk代理的对象 " + t + " 必须存在实现的接口！");
        }

        Handler handler = new Handler(t);
        return Proxy.newProxyInstance(classLoader, interfaces, handler);
    }

    public static <T> Object getProxy(T t, ICallback<T> callback) {
        ClassLoader classLoader = t.getClass().getClassLoader();
        Class<?>[] interfaces = t.getClass().getInterfaces();

        if (Objects.isNull(interfaces) || interfaces.length == 0) {
            throw new RuntimeException("实现的jdk代理的对象 " + t + " 必须存在实现的接口！");
        }

        Handler handler = new Handler(t, callback);
        return Proxy.newProxyInstance(classLoader, interfaces, handler);
    }

    /**
     * 拦截类
     */
    static class Handler implements InvocationHandler {

        private Object param;
        private ICallback callback;

        public Handler(Object param) {
            this.param = param;
        }

        public Handler(Object param, ICallback callback) {
            this.param = param;
            this.callback = callback;
        }

        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            if (Objects.isNull(callback)) {
                Object result = method.invoke(param, args);
                return result;
            }

            callback.doBeforeProxyInstance(param);
            Object result = method.invoke(param, args);
            callback.doAfterProxyInstance(param);
            return result;
        }
    }
}
